package sceneObjects;

import utils.*;
import main.Intersection;
import main.Parseable;
import main.Parser;

public class Sphere extends abstractSceneObject implements sceneObject, Parseable {
    private Point3D centerPoint;
    private double radius = 0;

    @Override
    public boolean setParameter(String name, String[] args) {
	if (super.setParameter(name, args)) {
	    return true;
	} else if (name.equals("center")) {
	    if (args.length != 3) {
		System.out.println("bad parameters");
		return false;
	    }
	    centerPoint = new Point3D(Parser.stringArrToDoubleArr(args));
	    return true;
	} else if (name.equals("radius")) {
	    if (args.length != 1) {
		System.out.println("bad parameters");
		return false;
	    }
	    radius = Double.parseDouble(args[0]);
	    return true;
	}

	// We didn't find any parameter
	return false;
    }

    public Sphere(Point3D p0, Vector normal, double radius) {
	this.centerPoint = new Point3D(p0);
	this.radius = radius;

	finalizeObject();
    }

    public Sphere() {

    }

    public Point3D getParameterization(Point3D p) {
	double x = p.getX();
	double y = p.getY();
	double z = p.getZ();

	double r = Math.sqrt(x * x + y * y + z * z);
	double theta = Math.acos(z / r) / Math.PI;
	double phi = ((Math.atan2(-y, -x)) + Math.PI) / (2 * Math.PI);

	phi = Math.min(phi, 0.99999);
	theta = Math.min(theta, 0.99999);

	return new Point3D(phi, theta, 0);
    }

    public Point3D getParameterization(Intersection intersect) {
	return getParameterization(intersect.getPoint());
    }

    @Override
    public boolean finalizeObject() {
	if (!super.finalizeObject()) {
	    return false;
	}
	// Checks if the 3 points exists
	if (centerPoint == null || radius <= 0) {
	    System.out.println("Error: one or more parameters are missing");
	    return false;
	}

	return true;
    }

    @Override
    public boolean isCamera() {
	return false;
    }

    public boolean isLight() {
	return false;
    }

    @Override
    public Intersection getIntersect(Ray ray) {

	Vector v = (new Vector(centerPoint, ray.getSource()));

	double a = 1;
	double b = ray.getDirection().scale(2).dotProduct(v);
	double c = v.dotProduct(v) - (radius * radius);
	double delta = ((b * b) - (4 * a * c));

	if (delta < 0) {
	    return null;
	}

	double t1 = (-b + Math.sqrt(delta)) / (2 * a);
	double t2 = (-b - Math.sqrt(delta)) / (2 * a);

	double tMin;
	if (t1 > 0 && t2 > 0) { // If both are a hit, take the minimum
	    tMin = Math.min(t1, t2);
	} else { // If at most one is a hit, the the maximum (if both aren't, we'll get 0)
	    tMin = Math.max(0, Math.max(t1, t2));
	}

	return (tMin == 0 ? null : new Intersection(ray.getPointAtDistance(tMin - 0.0001F), this));
    }

    public Vector getNormalAtPoint(Intersection intersect) {
	return new Vector(centerPoint, intersect.getPoint()).normalize();
    }
}
